package com.lsu.app.tool;


import com.lsu.app.bean.Course;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * @author hjj
 * @date 2023/10/16 20:55
 */
public class AESUtil {
    private static final String KEY_ALGORITHM = "AES";
    private static final String CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
    private static final String SECRET_KEY = "finance-platform"; // 16个字符，128位

    /**
     * 加密密文
     * @param plainPassword
     * @return
     * @throws Exception
     */
    public static String encrypt(String plainPassword) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        byte[] encrypted = cipher.doFinal(plainPassword.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encrypted);
    }

    /**
     * 确定角色
     * @param plainPassword
     * @param cipherPassword
     * @return
     * @throws Exception
     */
    public static boolean eq(String plainPassword,String cipherPassword) throws Exception {
        if(encrypt(plainPassword).equals(cipherPassword)){
            return true;
        }else{
            return false;
        }
    }
    /**
     * 解密密文
     * @param cipherPassword
     * @return
     * @throws Exception
     */
    public static String decrypt(String cipherPassword) throws Exception {
        byte[] bytes = Base64.getDecoder().decode(cipherPassword);
        SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), KEY_ALGORITHM);
        Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        byte[] decrypted = cipher.doFinal(bytes);
        return new String(decrypted, StandardCharsets.UTF_8);
    }

    /**
     * 按照V降序排序
     * @param map
     * @return
     * @param <K>
     * @param <V>
     */
    public static <K, V extends Comparable<? super V>> Map<K, V> sortDescend(Map<K, V> map) {
        List<Map.Entry<K, V>> list = new ArrayList<>(map.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
            @Override
            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
                int compare = (o1.getValue()).compareTo(o2.getValue());
                return -compare;
            }
        });

        Map<K, V> returnMap = new LinkedHashMap<K, V>();
        for (Map.Entry<K, V> entry : list) {
            returnMap.put(entry.getKey(), entry.getValue());
        }
        return returnMap;
    }
    /**
     * 获取当前周次
     * @param startDate
     * @param weekly
     * @param state
     * @return
     */
    public static Integer getCulWeekly(String startDate,Integer weekly,Integer state){
        if(state==0){
            return 0;
        }
        try{
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date date = sdf.parse(startDate);
            Date now = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());//当前日期
            if(date.after(now)){ //未到开始周
                return 1;
            }else {
                int days = (int) ((now.getTime()-date.getTime())/(24*60*60*1000));
                if((days/7)+1<weekly){
                    return (days/7)+1;
                }else{
                    return 1;
                }

            }
        }catch (ParseException e){
            return 0;
        }
    }

    /**
     * 获取此前日期的 月-日 字符串格式
     * @param days
     * @return
     */
    public static String[] getPreviousDates(int days) {
        List<String> dateList = new ArrayList<>();
        LocalDate today = LocalDate.now();
        LocalDate startDate = today.minusDays(days);
        while (startDate.isBefore(today)) {
            dateList.add(startDate.format(DateTimeFormatter.ofPattern("MM-dd")));
            startDate = startDate.plusDays(1);
        }
        return dateList.toArray(new String[0]);
    }

    /** 遗传算法
     * @param maps
     * @param courseIds
     * @return
     */
    public static int[][] geneticAlgorithm(Map<Integer,String[]> maps,List<Integer> courseIds){
        int[][] sets = new int[7][5];
        //随机生成祖先群落
        int size = courseIds.size();
        while (size != 0) {
            Random random = new Random(System.currentTimeMillis());
            int row = random.nextInt(7);
            int col = random.nextInt(5);
            if (sets[row][col] == 0) {
                sets[row][col] = courseIds.get(courseIds.size() - size);
                size--;
            }
        }
        int[][] test = new int[7][5];
        System.arraycopy(sets,0,test,0,sets.length);
        //设置迭代次数
        int cycles = 5;
        while (--cycles>=0){
            //选择运算
                //使用轮盘赌法进行选择
            int[] ints = countPro(test);//获得两个课表个体

            //交叉运算
            int co = 2;
            if(cycles%2==0){ //交换 遗传因子个数
                co=3;
            }
            List<Integer> swap1 = randGetInt(5,co);
            List<Integer> swap2 = randGetInt(5,co);
                //交换片段
                for(int i = 0;i<co;i++){
                    int swap = test[ints[0]][swap1.get(i)];
                    test[ints[0]][swap1.get(i)]=test[ints[1]][swap2.get(i)];
                    test[ints[1]][swap2.get(i)]=swap;
                }
            //变异运算
                if(Math.round(System.currentTimeMillis())%2==0){//概率事件
                    for(int i=0;i<new Random(System.currentTimeMillis()).nextInt(cycles);i++){//变异个体数 概率
                        int x1 = new Random(System.currentTimeMillis()).nextInt(5);
                        int y1 = new Random(System.currentTimeMillis()).nextInt(7);
                        int x2 = new Random(System.currentTimeMillis()).nextInt(5);
                        int y2 = new Random(System.currentTimeMillis()).nextInt(7);
                        //交换数据
                        int val = test[x1][y1];
                        test[x1][y1]=test[x2][y2];
                        test[x2][y2]=val;
                    }
                }
            //判断是否为良性繁衍
            if(upEvolve(sets,test,maps)){
                System.arraycopy(test,0,sets,0,sets.length);
            }
        }
        return sets;
    }

    /**
     * 是否 正向进化
     * @param sets
     * @param test
     * @return
     */
    private static boolean upEvolve(int[][] sets,int[][] test,Map<Integer,String[]> maps){
        float[] sets1 = {getEvo(sets[0],maps),getEvo(sets[1],maps),getEvo(sets[2],maps),getEvo(sets[3],maps),getEvo(sets[4],maps),getEvo(sets[5],maps),getEvo(sets[6],maps)};
        float[] test1 = {getEvo(test[0],maps),getEvo(test[1],maps),getEvo(test[2],maps),getEvo(test[3],maps),getEvo(test[4],maps),getEvo(test[5],maps),getEvo(test[6],maps)};
        return variance(sets1)<variance(test1);
    }
    private static float getEvo(int[] val,Map<Integer,String[]> maps){
        int i = 0;
        int sum = 0;
        for(int j=0;j<val.length;j++){
            if(val[j]!=0){
                for(int z=j+1;z<val.length;z++){
                    if(val[z]!=0){
                        sum+=getContrast(maps.get(val[j]),maps.get(val[z]));
                        i++;
                    }
                }
            }
        }
        if (i!=0){
            return (float)sum/i;
        }else{
            return 0;
        }
    }
    /**
     *  课程类型 对比度
     * @param strs1
     * @param strs2
     * @return
     */
    private static int getContrast(String[] strs1,String[] strs2){
        return (int) Arrays.stream(strs1)
                .filter(element -> Arrays.asList(strs2).contains(element))
                .count();
    }

    /**
     *  生成 0~max，个数为 number的数组(数据不能重复)
     * @param max
     * @param number
     * @return
     */
    private static List<Integer> randGetInt(int max,int number){
        if(max>number){ //个数得小于最大值
            List<Integer> values = new ArrayList<>();
            while (number>0){
                Random rand  = new Random(System.currentTimeMillis());
                Integer value = rand.nextInt(max);
                if(!values.contains(value)){
                    values.add(value);
                    number--;
                }
            }
            return values;
        }else{
            return null;
        }
    }

    /**
     * 计算个体占比值,并且随机抽取两个个体
     * @param sets
     * @return
     */
    private static int[] countPro(int[][] sets){
        int[] r = new int[sets.length];
        float[] re  = new float[sets.length];
        float[] lv = new float[sets.length];
        int all = 0;
        for (int i=0;i<r.length;i++) {
            r[i]=zeroCount(sets[i]);
            all+=r[i];
        }
        for (int i=0;i<r.length;i++) {
            re[i]= (float) r[i] /all;
            lv[i]=res(re,i);
        }
        int[] ret = new int[2];
        for (int i = 0;i<lv.length;i++){
            Random rand = new Random(System.currentTimeMillis());
            float sum1 = rand.nextFloat();
            if(sum1<lv[i]){
                ret[0]=i;
                break;
            }
        }
        while (true){
            Random rand = new Random(System.currentTimeMillis());
            float sum2 = rand.nextFloat();
            for (int i = 0;i<lv.length;i++){
                if(sum2<lv[i]){
                    ret[1]=i;
                    break;
                }
            }
            if(ret[0]!=ret[1]){
                return ret;
            }
        }
    }

    /**
     * 递归获取 占比范围
     * @param r
     * @param i
     * @return
     */
    private static float res(float[] r,int i){
        if(i>0){
            return res(r,i-1)+r[i];
        }else {
            return r[0];
        }
    }
    /**
     * 返回 数组中 0的个数
     * @param arr
     * @return
     */
    private static int zeroCount(int[] arr){
        int count = 0;
        for (int num : arr) {
            if (num == 0) {
                count++;
            }
        }
        return count;
    }
    /**
     * 传入一个数列x计算平均值
     * @param x
     * @return 平均值
     */
    private static float average(float[] x) {
        int n = x.length;            //数列元素个数
        float sum = 0;
        for (double i : x) {        //求和
            sum+=i;
        }
        return sum/n;
    }

    /**
     * 传入一个数列x计算方差
     * 方差s^2=[（x1-x）^2+（x2-x）^2+......（xn-x）^2]/（n）（x为平均数）
     * @param x 要计算的数列
     * @return 方差
     */
    private static float variance(float[] x) {
        int n = x.length;            //数列元素个数
        float avg = average(x);    //求平均值
        float var = 0;
        for (double i : x) {
            var += (i-avg)*(i-avg);    //（x1-x）^2+（x2-x）^2+......（xn-x）^2
        }
        return var/n;
    }

}
